# -*- coding: utf-8 -*-
# Copyright 2017-2018 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from collections import defaultdict

from odoo import api, fields, models, _
from odoo.exceptions import UserError
from odoo.osv import expression

ACC_SUM = 'sum'
ACC_AVG = 'avg'
ACC_NONE = 'none'


class MisKpiData(models.AbstractModel):
    """Abstract class for manually entered KPI values."""

    _name = 'mis.kpi.data'
    _description = 'MIS Kpi Data Abtract class'

    name = fields.Char(
        compute='_compute_name',
        required=False,
        readonly=True,
    )
    kpi_expression_id = fields.Many2one(
        comodel_name='mis.report.kpi.expression',
        required=True,
        ondelete='restrict',
        string="KPI",
    )
    date_from = fields.Date(
        required=True,
        string="From",
    )
    date_to = fields.Date(
        required=True,
        string="To",
    )
    amount = fields.Float(
    )
    seq1 = fields.Integer(
        related='kpi_expression_id.kpi_id.sequence',
        store=True,
        readonly=True,
    )
    seq2 = fields.Integer(
        related='kpi_expression_id.subkpi_id.sequence',
        store=True,
        readonly=True,
    )

    @api.depends('kpi_expression_id.subkpi_id.name',
                 'kpi_expression_id.kpi_id.name',
                 'date_from',
                 'date_to')
    def _compute_name(self):
        for rec in self:
            subkpi_name = rec.kpi_expression_id.subkpi_id.name
            if subkpi_name:
                subkpi_name = '.' + subkpi_name
            else:
                subkpi_name = ''
            rec.name = u'{}{}: {} - {}'.format(
                rec.kpi_expression_id.kpi_id.name,
                subkpi_name,
                rec.date_from,
                rec.date_to)

    def _intersect_days(self, dt_from, dt_to):
        item_dt_from = fields.Date.from_string(self.date_from)
        item_dt_to = fields.Date.from_string(self.date_to)
        item_days = (item_dt_to - item_dt_from).days + 1.0
        i_dt_from = max(dt_from, item_dt_from)
        i_dt_to = min(dt_to, item_dt_to)
        i_days = (i_dt_to - i_dt_from).days + 1.0
        return i_days, item_days

    @api.model
    def _query_kpi_data(self, date_from, date_to, base_domain):
        """Query mis.kpi.data over a time period.

        Returns {mis.report.kpi.expression: amount}
        """
        dt_from = fields.Date.from_string(date_from)
        dt_to = fields.Date.from_string(date_to)
        # all data items within or overlapping [date_from, date_to]
        date_domain = [
            ('date_from', '<=', date_to),
            ('date_to', '>=', date_from),
        ]
        domain = expression.AND([date_domain, base_domain])
        res = defaultdict(float)
        res_avg = defaultdict(list)
        for item in self.search(domain):
            i_days, item_days = item._intersect_days(dt_from, dt_to)
            if item.kpi_expression_id.kpi_id.accumulation_method == ACC_SUM:
                # accumulate pro-rata overlap between item and reporting period
                res[item.kpi_expression_id] += item.amount * i_days / item_days
            elif item.kpi_expression_id.kpi_id.accumulation_method == ACC_AVG:
                # memorize the amount and number of days overlapping
                # the reporting period (used as weight in average)
                res_avg[item.kpi_expression_id].append(
                    (i_days, item.amount))
            else:
                raise UserError(
                    _("Unexpected accumulation method %s for %s.") %
                    (item.kpi_expression_id.kpi_id.accumulation_method,
                     item.name))
        # compute weighted average for ACC_AVG
        for kpi_expression, amounts in res_avg.items():
            res[kpi_expression] = \
                sum(d * a for d, a in amounts) / sum(d for d, a in amounts)
        return res
